home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2005 October / PCWOCT05.iso / Software / FromTheMag / XAMPP 1.4.14 / xampp-win32-1.4.14-installer.exe / xampp / php / pear / Net / DNS / Resolver.php < prev    next >
PHP Script  |  2004-03-24  |  36KB  |  1,098 lines

  1. <?php
  2. /*
  3.  *  License Information:
  4.  *
  5.  *    Net_DNS:  A resolver library for PHP
  6.  *    Copyright (C) 2002 Eric Kilfoil eric@ypass.net
  7.  *
  8.  *    This library is free software; you can redistribute it and/or
  9.  *    modify it under the terms of the GNU Lesser General Public
  10.  *    License as published by the Free Software Foundation; either
  11.  *    version 2.1 of the License, or (at your option) any later version.
  12.  *
  13.  *    This library is distributed in the hope that it will be useful,
  14.  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16.  *    Lesser General Public License for more details.
  17.  *
  18.  *    You should have received a copy of the GNU Lesser General Public
  19.  *    License along with this library; if not, write to the Free Software
  20.  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  21.  */
  22.  
  23.  
  24. /* Net_DNS_Resolver object definition {{{ */
  25. /**
  26.  * A DNS Resolver library
  27.  *
  28.  * Resolver library.  Builds a DNS query packet, sends  the packet to the
  29.  * server and parses the reponse.
  30.  *
  31.  * @package Net_DNS
  32.  */
  33. class Net_DNS_Resolver
  34. {
  35.     /* class variable definitions {{{ */
  36.     /**
  37.      * An array of all nameservers to query
  38.      *
  39.      * An array of all nameservers to query
  40.      *
  41.      * @var array   $nameservers
  42.      * @access public
  43.      */
  44.     var $nameservers;
  45.     /**
  46.      * The UDP port to use for the query (default = 53)
  47.      *
  48.      * The UDP port to use for the query (default = 53)
  49.      *
  50.      * @var integer $port
  51.      * @access public
  52.      */
  53.     var $port;
  54.     /**
  55.      * The domain in which the resolver client host resides.
  56.      *
  57.      * The domain in which the resolver client host resides.
  58.      *
  59.      * @var string $domain
  60.      * @access public
  61.      */
  62.     var $domain;
  63.     /**
  64.      * The searchlist to apply to unqualified hosts
  65.      *
  66.      * An array of strings containg domains to apply to unqualified hosts
  67.      * passed to the resolver.
  68.      *
  69.      * @var array $searchlist
  70.      * @access public
  71.      */
  72.     var $searchlist;
  73.     /**
  74.      * The number of seconds between retransmission of unaswered queries
  75.      *
  76.      * The number of seconds between retransmission of unaswered queries
  77.      *
  78.      * @var integer $retrans
  79.      * @access public
  80.      */
  81.     var $retrans;
  82.     /**
  83.      * The number of times unanswered requests should be retried
  84.      *
  85.      * The number of times unanswered requests should be retried
  86.      *
  87.      * @var integer $retry
  88.      * @access public
  89.      */
  90.     var $retry;
  91.     /**
  92.      * Whether or not to use TCP (Virtual Circuits) instead of UDP
  93.      *
  94.      * If set to 0, UDP will be used unless TCP is required.  TCP is
  95.      * required for questions or responses greater than 512 bytes.
  96.      *
  97.      * @var boolean $usevc
  98.      * @access public
  99.      */
  100.     var $usevc;
  101.     /**
  102.      * Unknown
  103.      */
  104.     var $stayopen;
  105.     /**
  106.      * Ignore TC (truncated) bit
  107.      *
  108.      * If the server responds with the TC bit set on a response, and $igntc
  109.      * is set to 0, the resolver will automatically retransmit the request
  110.      * using virtual circuits (TCP).
  111.      *
  112.      * @access public
  113.      * @var boolean $igntc
  114.      */
  115.     var $igntc;
  116.     /**
  117.      * Recursion Desired
  118.      *
  119.      * Sets the value of the RD (recursion desired) bit in the header. If
  120.      * the RD bit is set to 0, the server will not perform recursion on the
  121.      * request.
  122.      *
  123.      * @var boolean $recurse
  124.      * @access public
  125.      */
  126.     var $recurse;
  127.     /**
  128.      * Unknown
  129.      */
  130.     var $defnames;
  131.     /**
  132.      * Unknown
  133.      */
  134.     var $dnsrch;
  135.     /**
  136.      * Contains the value of the last error returned by the resolver.
  137.      *
  138.      * Contains the value of the last error returned by the resolver.
  139.      *
  140.      * @var string $errorstring
  141.      * @access public
  142.      */
  143.     var $errorstring;
  144.     /**
  145.      * The origin of the packet.
  146.      *
  147.      * This contains a string containing the IP address of the name server
  148.      * from which the answer was given.
  149.      *
  150.      * @var string $answerfrom
  151.      * @access public
  152.      */
  153.     var $answerfrom;
  154.     /**
  155.      * The size of the answer packet.
  156.      *
  157.      * This contains a integer containing the size of the DNS packet the
  158.      * server responded with.
  159.      *
  160.      * @var string $answersize
  161.      * @access public
  162.      */
  163.     var $answersize;
  164.     /**
  165.      * The number of seconds after which a TCP connetion should timeout
  166.      *
  167.      * @var integer $tcp_timeout
  168.      * @access public
  169.      */
  170.     var $tcp_timeout;
  171.     /**
  172.      * The location of the system resolv.conf file.
  173.      *
  174.      * The location of the system resolv.conf file.
  175.      * 
  176.      * @var string $resolv_conf
  177.      */
  178.     var $resolv_conf = "/etc/resolv.conf";
  179.     /**
  180.      * The name of the user defined resolv.conf
  181.      *
  182.      * The resolver will attempt to look in both the current directory as
  183.      * well as the user's home directory for a user defined resolver
  184.      * configuration file
  185.      *
  186.      * @var string $dotfile
  187.      * @see Net_DNS_Resolver::$confpath
  188.      */
  189.     var $dotfile = ".resolv.conf";
  190.     /**
  191.      * A array of directories to search for the user's resolver config
  192.      *
  193.      * A array of directories to search for the user's resolver config
  194.      *
  195.      * @var string $confpath
  196.      * @see Net_DNS_Resolver::$dotfile
  197.      */
  198.     var $confpath;
  199.     /**
  200.      * debugging flag
  201.      *
  202.      * If set to TRUE (non-zero), debugging code will be displayed as the
  203.      * resolver makes the request.
  204.      *
  205.      * @var boolean $debug;
  206.      * @access public
  207.      */
  208.     var $debug;
  209.     /**
  210.      * use the (currently) experimental PHP socket library
  211.      *
  212.      * If set to TRUE (non-zero), the Resolver will attempt to use the
  213.      * much more effecient PHP sockets extension (if available).
  214.      *
  215.      * @var boolean $useEnhancedSockets;
  216.      * @access public
  217.      */
  218.     var $useEnhancedSockets = 1;
  219.  
  220.     /* }}} */
  221.     /* class constructor - Net_DNS_Resolver() {{{ */
  222.     /**
  223.      * Initializes the Resolver Object
  224.      */
  225.     function Net_DNS_Resolver()
  226.     {
  227.         $default = array(
  228.                 "nameservers" => array(),
  229.                 "port"    => "53",
  230.                 "domain"  => "",
  231.                 "searchlist"  => array(),
  232.                 "retrans" => 5,
  233.                 "retry"   => 4,
  234.                 "usevc"   => 0,
  235.                 "stayopen"  => 0,
  236.                 "igntc"   => 0,
  237.                 "recurse" => 1,
  238.                 "defnames"  => 1,
  239.                 "dnsrch"  => 1,
  240.                 "debug"   => 0,
  241.                 "errorstring" => "unknown error or no error",
  242.                 "answerfrom"    => "",
  243.                 "answersize"    => 0,
  244.                 "tcp_timeout"   => 120
  245.                 );
  246.         foreach ($default as $k => $v) {
  247.             $this->{$k} = $v;
  248.         }
  249.         $this->confpath[0] = getenv("HOME");
  250.         $this->confpath[1] = ".";
  251.         $this->res_init();
  252.     }
  253.  
  254.     /* }}} */
  255.     /* Net_DNS_Resolver::res_init() {{{ */
  256.     /**
  257.      * Initalizes the resolver library
  258.      *
  259.      * res_init() searches for resolver library configuration files  an
  260.      * initializes the various properties of the resolver object.
  261.      *
  262.      * @see Net_DNS_Resolver::$resolv_conf, Net_DNS_Resolver::$dotfile,
  263.      *      Net_DNS_Resolver::$confpath, Net_DNS_Resolver::$searchlist,
  264.      *      Net_DNS_Resolver::$domain, Net_DNS_Resolver::$nameservers
  265.      * @access public
  266.      */
  267.     function res_init()
  268.     {
  269.         $err = error_reporting(0);
  270.         if (file_exists($this->resolv_conf) && is_readable($this->resolv_conf)) {
  271.             $this->read_config($this->resolv_conf);
  272.         }
  273.  
  274.         foreach ($this->confpath as $dir) {
  275.             $file = "$dir/" . $this->dotfile;
  276.             if (file_exists($file) && is_readable($file)) {
  277.                 $this->read_config($file);
  278.             }
  279.         }
  280.  
  281.         $this->read_env();
  282.  
  283.         if (!strlen($this->domain) && strlen($this->searchlist)) {
  284.             $this->default{"domain"} = $this->default{"searchlist"}[0];
  285.         } else if (! strlen($this->searchlist) && strlen($this->domain)) {
  286.             $this->searchlist = array($this->domain);
  287.         }
  288.         error_reporting($err);
  289.     }
  290.  
  291.     /* }}} */
  292.     /* Net_DNS_Resolver::read_config {{{ */
  293.     /**
  294.      * Reads and parses a resolver configuration file
  295.      *
  296.      * @param string $file The name of the file to open and parse
  297.      */
  298.     function read_config($file)
  299.     {
  300.         if (! ($f = fopen($file, "r"))) {
  301.             $this->error = "can't open $file";
  302.         }
  303.  
  304.         while (! feof($f)) {
  305.             $line = chop(fgets($f, 10240));
  306.             $line = ereg_replace("(.*)[;#].*", "\\1", $line);
  307.             if (ereg("^[ \t]*$", $line, $regs)) {
  308.                 continue;
  309.             }
  310.             ereg("^[ \t]*([^ \t]+)[ \t]+([^ \t]+)", $line, $regs);
  311.             $option = $regs[1];
  312.             $value = $regs[2];
  313.  
  314.             switch ($option) {
  315.                 case 'domain':
  316.                     $this->domain = $regs[2];
  317.                     break;
  318.                 case 'search':
  319.                     $this->searchlist[count($this->searchlist)] = $regs[2];
  320.                     break;
  321.                 case 'nameserver':
  322.                     foreach (split(" ", $regs[2]) as $ns)
  323.                         $this->nameservers[count($this->nameservers)] = $ns;
  324.                     break;
  325.             }
  326.         }
  327.         fclose($f);
  328.     }
  329.  
  330.     /* }}} */
  331.     /* Net_DNS_Resolver::read_env() {{{ */
  332.     /**
  333.      * Examines the environment for resolver config information
  334.      */
  335.     function read_env()
  336.     {
  337.         if (getenv("RES_NAMESERVERS")) {
  338.             $this->nameservers = split(" ", getenv("RES_NAMESERVERS"));
  339.         }
  340.  
  341.         if (getenv("RES_SEARCHLIST")) {
  342.             $this->searchlist = split(" ", getenv("RES_SEARCHLIST"));
  343.         }
  344.  
  345.         if (getenv("LOCALDOMAIN")) {
  346.             $this->domain = getenv("LOCALDOMAIN");
  347.         }
  348.  
  349.         if (getenv("RES_OPTIONS")) {
  350.             $env = split(" ", getenv("RES_OPTIONS"));
  351.             foreach ($env as $opt) {
  352.                 list($name, $val) = split(":", $opt);
  353.                 if ($val == "") {
  354.                     $val = 1;
  355.                 }
  356.                 $this->{$name} = $val;
  357.             }
  358.         }
  359.     }
  360.  
  361.     /* }}} */
  362.     /* Net_DNS_Resolver::string() {{{ */
  363.     /**
  364.      * Builds a string containing the current state of the resolver
  365.      *
  366.      * Builds formatted string containing the state of the resolver library suited
  367.      * for display.
  368.      *
  369.      * @access public
  370.      */
  371.     function string()
  372.     {
  373.         $state = ";; Net_DNS_Resolver state:\n";
  374.         $state .= ";;  domain       = " . $this->domain . "\n";
  375.         $state .= ";;  searchlist   = " . implode(" ", $this->searchlist) . "\n";
  376.         $state .= ";;  nameservers  = " . implode(" ", $this->nameservers) . "\n";
  377.         $state .= ";;  port         = " . $this->port . "\n";
  378.         $state .= ";;  tcp_timeout  = ";
  379.         $state .= ($this->tcp_timeout ? $this->tcp_timeout : "indefinite") . "\n";
  380.         $state .= ";;  retrans  = " . $this->retrans . "  ";
  381.         $state .= "retry    = " . $this->retry . "\n";
  382.         $state .= ";;  usevc    = " . $this->usevc . "  ";
  383.         $state .= "stayopen = " . $this->stayopen . "    ";
  384.         $state .= "igntc = " . $this->igntc . "\n";
  385.         $state .= ";;  defnames = " . $this->defnames . "  ";
  386.         $state .= "dnsrch   = " . $this->dnsrch . "\n";
  387.         $state .= ";;  recurse  = " . $this->recurse . "  ";
  388.         $state .= "debug    = " . $this->debug . "\n";
  389.         return($state);
  390.     }
  391.  
  392.     /* }}} */
  393.     /* Net_DNS_Resolver::nextid() {{{ */
  394.     /**
  395.      * Returns the next request Id to be used for the DNS packet header
  396.      */
  397.     function nextid()
  398.     {
  399.         global $_Net_DNS_packet_id;
  400.  
  401.         return($_Net_DNS_packet_id++);
  402.     }
  403.     /* }}} */
  404.     /* not completed - Net_DNS_Resolver::nameservers() {{{ */
  405.     /**
  406.      * Unknown - not ported yet
  407.      */
  408.     function nameservers($nsa)
  409.     {
  410.         $defres = new Net_DNS_Resolver();
  411.  
  412.         if (is_array($ns)) {
  413.             foreach ($nsa as $ns) {
  414.                 if (ereg("^[0-9]+(\.[0-9]+){0,3}$", $ns, $regs)) {
  415.                     $newns[count($newns)] = $ns;
  416.                 } else {
  417.                     /* 
  418.                      * This still needs to be ported
  419.                      *
  420.                      if ($ns !~ /\./) {
  421.                      if (defined $defres->searchlist) {
  422.                      @names = map { $ns . "." . $_ }
  423.                      $defres->searchlist;
  424.                      }
  425.                      elsif (defined $defres->domain) {
  426.                      @names = ($ns . "." . $defres->domain);
  427.                      }
  428.                      }
  429.                      else {
  430.                      @names = ($ns);
  431.                      }
  432.  
  433.                      my $packet = $defres->search($ns);
  434.                      $this->errorstring($defres->errorstring);
  435.                      if (defined($packet)) {
  436.                      push @a, cname_addr([@names], $packet);
  437.                      }
  438.                  */
  439.                 }
  440.             }
  441.             $this->nameservers = $nsa;
  442.         }
  443.         return($this->nameservers);
  444.     }
  445.  
  446.     /* }}} */
  447.     /* not completed - Net_DNS_Resolver::cname_addr() {{{ */
  448.     /**
  449.      * Unknown - not ported yet
  450.      */
  451.     function cname_addr()
  452.     {
  453.     }
  454.     /* }}} */
  455.     /* Net_DNS_Resolver::search() {{{ */
  456.     /**
  457.      * Searches nameservers for an answer
  458.      *
  459.      * Goes through the search list and attempts to resolve name based on
  460.      * the information in the search list.
  461.      *
  462.      * @param string $name The name (LHS) of a resource record to query.
  463.      * @param string $type The type of record to query.
  464.      * @param string $class The class of record to query.
  465.      * @return mixed    an object of type Net_DNS_Packet on success,
  466.      *                  or FALSE on failure.
  467.      * @see Net_DNS::typesbyname(), Net_DNS::classesbyname()
  468.      * @access public
  469.      */
  470.     function search($name, $type = "A", $class = "IN")
  471.     {
  472.         /*
  473.          * If the name looks like an IP address then do an appropriate
  474.          * PTR query.
  475.          */
  476.         if (preg_match("/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/", $name, $regs)) {
  477.             $name = "$regs[4].$regs[3].$regs[2].$regs[1].in-addr.arpa";
  478.             $type = "PTR";
  479.         }
  480.  
  481.         /*
  482.          * If the name contains at least one dot then try it as is first.
  483.          */
  484.         if (strchr($name, ".")) {
  485.             if ($this->debug) {
  486.                 echo ";; search($name, $type, $class)\n";
  487.             }
  488.             $ans = $this->query($name, $type, $class);
  489.             if ((is_object($ans)) && $ans->header->ancount > 0) {
  490.                 return($ans);
  491.             }
  492.         }
  493.  
  494.         /*
  495.          * If the name doesn't end in a dot then apply the search list.
  496.          */
  497.         $domain = "";
  498.         if ((! preg_match("/\.$/", $name)) && $this->dnsrch) {
  499.             foreach ($this->searchlist as $domain) {
  500.                 $newname = "$name.$domain";
  501.                 if ($this->debug) {
  502.                     echo ";; search($newname, $type, $class)\n";
  503.                 }
  504.                 $ans = $this->query($newname, $type, $class);
  505.                 if ((is_object($ans)) && $ans->header->ancount > 0) {
  506.                     return($ans);
  507.                 }
  508.             }
  509.         }
  510.  
  511.         /*
  512.          * Finally, if the name has no dots then try it as is.
  513.          */
  514.         if (! strlen(strchr($name, "."))) {
  515.             if ($this->debug) {
  516.                 echo ";; search($name, $type, $class)\n";
  517.             }
  518.             $ans = $this->query("$name.", $type, $class);
  519.             if (($ans = $this->query($name, $type, $class)) &&
  520.                     $ans->header->ancount > 0) {
  521.                 return($ans);
  522.             }
  523.         }
  524.  
  525.         /*
  526.          * No answer was found.
  527.          */
  528.         return(0);
  529.     }
  530.  
  531.     /* }}} */
  532.     /* Net_DNS_Resolver::query() {{{ */
  533.     /**
  534.      * Queries nameservers for an answer
  535.      *
  536.      * Queries the nameservers listed in the resolver configuration for  an
  537.      * answer to a question packet.
  538.      *
  539.      * @param string $name The name (LHS) of a resource record to query.
  540.      * @param string $type The type of record to query.
  541.      * @param string $class The class of record to query.
  542.      * @return mixed    an object of type Net_DNS_Packet on success,
  543.      *                  or FALSE on failure.
  544.      * @see Net_DNS::typesbyname(), Net_DNS::classesbyname()
  545.      * @access public
  546.      */
  547.     function query($name, $type = "A", $class = "IN")
  548.     {
  549.         /*
  550.          * If the name doesn't contain any dots then append the default domain.
  551.          */
  552.         if ((strchr($name, ".") < 0) && $this->defnames) {
  553.             $name .= "." . $this->domain;
  554.         }
  555.  
  556.         /*
  557.          * If the name looks like an IP address then do an appropriate
  558.          * PTR query.
  559.          */
  560.         if (preg_match("/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/", $name, $regs)) {
  561.             $name = "$regs[4].$regs[3].$regs[2].$regs[1].in-addr.arpa";
  562.             $type = "PTR";
  563.         }
  564.  
  565.         if ($this->debug) {
  566.             echo ";; query($name, $type, $class)\n";
  567.         }
  568.         $packet = new Net_DNS_Packet($this->debug);
  569.         $packet->buildQuestion($name, $type, $class);
  570.         $packet->header->rd = $this->recurse;
  571.         $ans = $this->send($packet);
  572.         if (is_object($ans) && $ans->header->ancount > 0) {
  573.             return($ans);
  574.         }
  575.         return(0);
  576.     }
  577.  
  578.     /* }}} */
  579.     /* Net_DNS_Resolver::send($packetORname, $qtype = "", $qclass = "") {{{ */
  580.     /**
  581.      * Sends a packet to a nameserver
  582.      *
  583.      * Determines the appropriate communication method (UDP or TCP) and
  584.      * send a DNS packet to a nameserver.  Use of the this function
  585.      * directly  is discouraged. $packetORname should always be a properly
  586.      * formatted binary DNS packet.  However, it is possible to send  a
  587.      * query here and bypass Net_DNS_Resolver::query()
  588.      *
  589.      * @param string $packetORname      A binary DNS packet stream or a
  590.      *                                  hostname to query
  591.      * @param string $qtype     This should not be used
  592.      * @param string $qclass    This should not be used
  593.      * @return object Net_DNS_Packet    An answer packet object
  594.      */
  595.     function send($packetORname, $qtype = "", $qclass = "")
  596.     {
  597.         $packet = $this->make_query_packet($packetORname, $qtype, $qclass);
  598.         $packet_data = $packet->data();
  599.  
  600.         if ($this->usevc != 0 || strlen($packet_data > 512)) {
  601.             $ans = $this->send_tcp($packet, $packet_data);
  602.         } else {
  603.             $ans = $this->send_udp($packet, $packet_data);
  604.  
  605.             if ($ans && $ans->header->tc && $this->igntc != 0) {
  606.                 if ($this->debug) {
  607.                     echo ";;\n;; packet truncated: retrying using TCP\n";
  608.                 }
  609.                 $ans = $this->send_tcp($packet, $packet_data);
  610.             }
  611.         }
  612.         return($ans);
  613.     }
  614.  
  615.     /* }}} */
  616.     /* Net_DNS_Resolver::printhex($packet_data) {{{ */
  617.     /**
  618.      * Sends a packet via TCP to the list of name servers.
  619.      */
  620.     function printhex($data)
  621.     {
  622.         $data = "  " . $data;
  623.         $start = 0;
  624.         while ($start < strlen($data)) {
  625.             printf(";; %03d: ", $start);
  626.             for ($ctr = $start; $ctr < $start+16; $ctr++) {
  627.                 if ($ctr < strlen($data))
  628.                     printf("%02x ", ord($data[$ctr]));
  629.                 else
  630.                     echo "   ";
  631.             }
  632.             echo "   ";
  633.             for ($ctr = $start; $ctr < $start+16; $ctr++) {
  634.                 if (ord($data[$ctr]) < 32 || ord($data[$ctr]) > 127) {
  635.                     echo ".";
  636.                 } else {
  637.                     echo $data[$ctr];
  638.                 }
  639.             }
  640.             echo "\n";
  641.             $start += 16;
  642.         }
  643.     }
  644.     /* }}} */
  645.     /* Net_DNS_Resolver::send_tcp($packet, $packet_data) {{{ */
  646.     /**
  647.      * Sends a packet via TCP to the list of name servers.
  648.      *
  649.      * @param string $packet    A packet object to send to the NS list
  650.      * @param string $packet_data   The data in the packet as returned by
  651.      *                              the Net_DNS_Packet::data() method
  652.      * @return object Net_DNS_Packet Returns an answer packet object
  653.      * @see Net_DNS_Resolver::send_udp(), Net_DNS_Resolver::send()
  654.      */
  655.     function send_tcp($packet, $packet_data)
  656.     {
  657.         if (! count($this->nameservers)) {
  658.             $this->errorstring = "no nameservers";
  659.             if ($this->debug) {
  660.                 echo ";; ERROR: send_tcp: no nameservers\n";
  661.             }
  662.             return(NULL);
  663.         }
  664.         $timeout = $this->tcp_timeout;
  665.  
  666.         foreach ($this->nameservers as $ns) {
  667.             $srcport = $this->srcport;
  668.             $srcaddr = $this->srcaddr;
  669.             $dstport = $this->port;
  670.             if ($this->debug) {
  671.                 echo ";; send_tcp($ns:$dstport) (src port = $srcport)\n";
  672.             }
  673.             $sock_key = "$ns:$dstport";
  674.             if ($this->persistent_tcp &&
  675.                     is_resource($this->sockets[$sock_key])) {
  676.                 $sock = &$this->sockets[$sock_key];
  677.             } else {
  678.                 if (! ($sock = fsockopen($ns, $dstport, $errno,
  679.                                 $errstr, $timeout))) {
  680.                     $this->errorstring = "connection failed";
  681.                     if ($this->debug) {
  682.                         echo ";; ERROR: send_tcp: connection failed: $errstr\n";
  683.                     }
  684.                     continue;
  685.                 }
  686.                 $this->sockets[$sock_key] = $sock;
  687.                 unset($sock);
  688.                 $sock = &$this->sockets[$sock_key];
  689.             }
  690.             $lenmsg = pack("n", strlen($packet_data));
  691.             if ($this->debug) {
  692.                 echo ";; sending " . strlen($packet_data) . " bytes\n";
  693.             }
  694.  
  695.             if (($sent = fwrite($sock, $lenmsg)) == -1) {
  696.                 $this->errorstring = "length send failed";
  697.                 if ($this->debug) {
  698.                     echo ";; ERROR: send_tcp: length send failed\n";
  699.                 }
  700.                 continue;
  701.             }
  702.  
  703.             if (($sent = fwrite($sock, $packet_data)) == -1) {
  704.                 $this->errorstring = "packet send failed";
  705.                 if ($this->debug) {
  706.                     echo ";; ERROR: send_tcp: packet data send failed\n";
  707.                 }
  708.             }
  709.  
  710.             socket_set_timeout($sock, $timeout);
  711.             $buf = fread($sock, 2);
  712.             $e = socket_get_status($sock);
  713.             $len = unpack("nint", $buf);
  714.             $len = $len["int"];
  715.             if (!$len) {
  716.                 continue;
  717.             }
  718.             $buf = fread($sock, $len);
  719.             $actual = strlen($buf);
  720.             $this->answerfrom = $ns;
  721.             $this->answersize = $len;
  722.             if ($this->debug) {
  723.                 echo ";; received $actual bytes\n";
  724.             }
  725.             if ($actual != $len) {
  726.                 $this->errorstring = "expected $len bytes, received $buf";
  727.                 if ($this->debug) {
  728.                     echo ";; send_tcp: " . $this->errorstring;
  729.                 }
  730.                 continue;
  731.             }
  732.  
  733.             $ans = new Net_DNS_Packet($this->debug);
  734.             if (is_null($ans->parse($buf))) {
  735.                 continue;
  736.             }
  737.             $this->errorstring = $ans->header->rcode;
  738.             $ans->answerfrom = $this->answerfrom;
  739.             $ans->answersize = $this->answersize;
  740.             return($ans);
  741.         }
  742.     }
  743.  
  744.     /* }}} */
  745.     /* Net_DNS_Resolver::send_udp_no_sock_lib($packet, $packet_data) {{{ */
  746.     /**
  747.      * Sends a packet via UDP to the list of name servers.
  748.      *
  749.      * This function sends a packet to a nameserver.  It is called by
  750.      * send_udp if the sockets PHP extension is not compiled into PHP.
  751.      *
  752.      * @param string $packet    A packet object to send to the NS list
  753.      * @param string $packet_data   The data in the packet as returned by
  754.      *                              the Net_DNS_Packet::data() method
  755.      * @return object Net_DNS_Packet Returns an answer packet object
  756.      * @see Net_DNS_Resolver::send_tcp(), Net_DNS_Resolver::send(),
  757.      *      Net_DNS_Resolver::send_udp(), Net_DNS_Resolver::send_udp_with_sock_lib()
  758.      */
  759.     function send_udp_no_sock_lib($packet, $packet_data)
  760.     {
  761.         $retrans = $this->retrans;
  762.         $timeout = $retrans;
  763.  
  764.         /*
  765.          * PHP doesn't have excellent socket support as of this writing.
  766.          * This needs to be rewritten when PHP POSIX socket support is
  767.          * complete.
  768.          * Obviously, this code is MUCH different than the PERL implementation
  769.          */
  770.  
  771.         $w = error_reporting(0);
  772.         $ctr = 0;
  773.         // Create a socket handle for each nameserver
  774.         foreach ($this->nameservers as $nameserver) {
  775.             if ($sock[$ctr++] = fsockopen("udp://$nameserver", $this->port)) {
  776.                 $peerhost[$ctr-1] = $nameserver;
  777.                 $peerport[$ctr-1] = $this->port;
  778.                 socket_set_blocking($sock, FALSE);
  779.             } else {
  780.                 $ctr--;
  781.             }
  782.         }
  783.         error_reporting($w);
  784.  
  785.         if ($ctr == 0) {
  786.             $this->errorstring = "no nameservers";
  787.             return(NULL);
  788.         }
  789.  
  790.         for ($i = 0; $i < $this->retry; $i++, $retrans *= 2,
  791.                 $timeout = (int) ($retrans / (count($ns)+1))) {
  792.             if ($timeout < 1) {
  793.                 $timeout = 1;
  794.             }
  795.  
  796.             foreach ($sock as $k => $s) {
  797.                 if ($this->debug) {
  798.                     echo ";; send_udp(" . $peerhost[$k] . ":" . $peerport[$k] . "): sending " . strlen($packet_data) . " bytes\n";
  799.                 }
  800.  
  801.                 if (! fwrite($s, $packet_data)) {
  802.                     if ($this->debug) {
  803.                         echo ";; send error\n";
  804.                     }
  805.                 }
  806.  
  807.                 /*
  808.                  *  Here's where it get's really nasty.  We don't have a select()
  809.                  *  function here, so we have to poll for a response... UGH!
  810.                  */
  811.  
  812.                 $timetoTO  = time() + (double)microtime() + $timeout;
  813.  
  814.                 /*
  815.                  * let's sleep for a few hundred microseconds to let the
  816.                  * data come in from the network...
  817.                  */
  818.                 usleep(500);
  819.                 $buf = "";
  820.                 while (! strlen($buf) && $timetoTO > (time() +
  821.                             (double)microtime())) {
  822.                     socket_set_blocking($s, FALSE);
  823.                     if ($buf = fread($s, 512)) {
  824.                         $this->answerfrom = $peerhost[$k];
  825.                         $this->answersize = strlen($buf);
  826.                         if ($this->debug) {
  827.                             echo ";; answer from " . $peerhost[$k] . ":" .
  828.                                 $peerport[$k] .  ": " . strlen($buf) . " bytes\n";
  829.                         }
  830.                         $ans = new Net_DNS_Packet($this->debug);
  831.                         if ($ans->parse($buf)) {
  832.                             if ($ans->header->qr != "1") {
  833.                                 continue;
  834.                             }
  835.                             if ($ans->header->id != $packet->header->id) {
  836.                                 continue;
  837.                             }
  838.                             $this->errorstring = $ans->header->rcode;
  839.                             $ans->answerfrom = $this->answerfrom;
  840.                             $ans->answersize = $this->answersize;
  841.                             return($ans);
  842.                         }
  843.                     }
  844.                     // Sleep another 1/100th of a second... this sucks...
  845.                     usleep(1000);
  846.                 }
  847.  
  848.                 $this->errorstring = "query timed out";
  849.                 return(NULL);
  850.             }
  851.         }
  852.     }
  853.  
  854.     /* }}} */
  855.     /* Net_DNS_Resolver::send_udp_with_sock_lib($packet, $packet_data) {{{ */
  856.     /**
  857.      * Sends a packet via UDP to the list of name servers.
  858.      *
  859.      * This function sends a packet to a nameserver.  It is called by
  860.      * send_udp if the sockets PHP extension is compiled into PHP.
  861.      *
  862.      * @param string $packet    A packet object to send to the NS list
  863.      * @param string $packet_data   The data in the packet as returned by
  864.      *                              the Net_DNS_Packet::data() method
  865.      * @return object Net_DNS_Packet Returns an answer packet object
  866.      * @see Net_DNS_Resolver::send_tcp(), Net_DNS_Resolver::send(),
  867.      *      Net_DNS_Resolver::send_udp(), Net_DNS_Resolver::send_udp_no_sock_lib()
  868.      */
  869.     function send_udp_with_sock_lib($packet, $packet_data)
  870.     {
  871.         $retrans = $this->retrans;
  872.         $timeout = $retrans;
  873.  
  874.         //$w = error_reporting(0);
  875.         $ctr = 0;
  876.         // Create a socket handle for each nameserver
  877.         foreach ($this->nameservers as $nameserver) {
  878.             if ((($sock[$ctr++] = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP)) >= 0) &&
  879.                     socket_connect($sock[$ctr-1], $nameserver, $this->port) >= 0) {
  880.                 $peerhost[$ctr-1] = $nameserver;
  881.                 $peerport[$ctr-1] = $this->port;
  882.                 socket_set_nonblock($sock[$ctr-1]);
  883.             } else {
  884.                 $ctr--;
  885.             }
  886.         }
  887.         //error_reporting($w);
  888.  
  889.         if ($ctr == 0) {
  890.             $this->errorstring = "no nameservers";
  891.             return(NULL);
  892.         }
  893.  
  894.         for ($i = 0; $i < $this->retry; $i++, $retrans *= 2,
  895.                 $timeout = (int) ($retrans / (count($ns)+1))) {
  896.             if ($timeout < 1) {
  897.                 $timeout = 1;
  898.             }
  899.  
  900.             foreach ($sock as $k => $s) {
  901.                 if ($this->debug) {
  902.                     echo ";; send_udp(" . $peerhost[$k] . ":" . $peerport[$k] . "): sending " . strlen($packet_data) . " bytes\n";
  903.                 }
  904.  
  905.                 if (! socket_write($s, $packet_data)) {
  906.                     if ($this->debug) {
  907.                         echo ";; send error\n";
  908.                     }
  909.                 }
  910.  
  911.                 $set = array($s);
  912.                 if ($this->debug) {
  913.                     echo ";; timeout set to $timeout seconds\n";
  914.                 }
  915.                 $changed = socket_select($set, $w = null, $e = null, $timeout);
  916.                 if ($changed) {
  917.                     $buf = socket_read($set[0], 512);
  918.                     $this->answerfrom = $peerhost[$k];
  919.                     $this->answersize = strlen($buf);
  920.                     if ($this->debug) {
  921.                         echo ";; answer from " . $peerhost[$k] . ":" .
  922.                             $peerport[$k] .  ": " . strlen($buf) . " bytes\n";
  923.                     }
  924.                     $ans = new Net_DNS_Packet($this->debug);
  925.                     if ($ans->parse($buf)) {
  926.                         if ($ans->header->qr != "1") {
  927.                             continue;
  928.                         }
  929.                         if ($ans->header->id != $packet->header->id) {
  930.                             continue;
  931.                         }
  932.                         $this->errorstring = $ans->header->rcode;
  933.                         $ans->answerfrom = $this->answerfrom;
  934.                         $ans->answersize = $this->answersize;
  935.                         return($ans);
  936.                     }
  937.                 }
  938.  
  939.                 $this->errorstring = "query timed out";
  940.                 return(NULL);
  941.             }
  942.         }
  943.     }
  944.  
  945.     /* }}} */
  946.     /* Net_DNS_Resolver::send_udp($packet, $packet_data) {{{ */
  947.     /**
  948.      * Sends a packet via UDP to the list of name servers.
  949.      *
  950.      * This function sends a packet to a nameserver.  send_udp calls
  951.      * either Net_DNS_Resolver::send_udp_no_sock_lib() or
  952.      * Net_DNS_Resolver::send_udp_with_sock_lib() depending on whether or
  953.      * not the sockets extension is compiled into PHP.  Note that using the
  954.      * sockets extension is MUCH more effecient.
  955.      *
  956.      * @param object Net_DNS_Packet $packet A packet object to send to the NS list
  957.      * @param string $packet_data   The data in the packet as returned by
  958.      *                              the Net_DNS_Packet::data() method
  959.      * @return object Net_DNS_Packet Returns an answer packet object
  960.      * @see Net_DNS_Resolver::send_tcp(), Net_DNS_Resolver::send(),
  961.      *      Net_DNS_Resolver::send_udp(), Net_DNS_Resolver::send_udp_no_sock_lib()
  962.      */
  963.     function send_udp($packet, $packet_data)
  964.     {
  965.         if (extension_loaded("sockets") && $this->useEnhancedSockets) {
  966.             if ($this->debug) {
  967.                 echo "\n;; using extended PHP sockets\n";
  968.             }
  969.             return($this->send_udp_with_sock_lib($packet, $packet_data));
  970.         } else {
  971.             if ($this->debug) {
  972.                 echo "\n;; using simple sockets\n";
  973.             }
  974.             return($this->send_udp_no_sock_lib($packet, $packet_data));
  975.         }
  976.     }
  977.  
  978.     /* }}} */
  979.     /* not completed - Net_DNS_Resolver::bgsend() {{{ */
  980.     /**
  981.      * Unknown - not ported yet
  982.      */
  983.     function bgsend()
  984.     {
  985.     }
  986.  
  987.     /* }}} */
  988.     /* not completed - Net_DNS_Resolver::bgread() {{{ */
  989.     /**
  990.      * Unknown - not ported yet
  991.      */
  992.     function bgread()
  993.     {
  994.     }
  995.     /* }}} */
  996.     /* not completed - Net_DNS_Resolver::bgisready() {{{ */
  997.     /**
  998.      * Unknown - not ported yet
  999.      */
  1000.     function bgisready()
  1001.     {
  1002.     }
  1003.     /* }}} */
  1004.     /* Net_DNS_Resolver::make_query_packet($packetORname, $type = "", $class = "") {{{ */
  1005.     /**
  1006.      * Unknown
  1007.      */
  1008.     function make_query_packet($packetORname, $type = "", $class = "")
  1009.     {
  1010.         if (is_object($packetORname) && get_class($packetORname) == "net_dns_packet") {
  1011.             $packet = $packetORname;
  1012.         } else {
  1013.             $name = $packetORname;
  1014.             if ($type == "") {
  1015.                 $type = "A";
  1016.             }
  1017.             if ($class == "") {
  1018.                 $class = "IN";
  1019.             }
  1020.  
  1021.             /*
  1022.              * If the name looks like an IP address then do an appropriate
  1023.              * PTR query.
  1024.              */
  1025.             if (preg_match("/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/", $name, $regs)) {
  1026.                 $name = "$regs[4].$regs[3].$regs[2].$regs[1].in-addr.arpa";
  1027.                 $type = "PTR";
  1028.             }
  1029.  
  1030.             if ($this->debug) {
  1031.                 echo ";; query($name, $type, $class)\n";
  1032.             }
  1033.             $packet = new Net_DNS_Packet($this->debug);
  1034.             $packet->buildQuestion($name, $type, $class);
  1035.         }
  1036.  
  1037.         $packet->header->rd = $this->recurse;
  1038.  
  1039.         return($packet);
  1040.     }
  1041.  
  1042.     /* }}} */
  1043.     /* Net_DNS_Resolver::axfr() {{{ */
  1044.     /**
  1045.      * Performs an AXFR query (zone transfer)
  1046.      *
  1047.      * Requests a zone transfer from the nameservers. Note that zone
  1048.      * transfers will ALWAYS use TCP regardless of the setting of the
  1049.      * Net_DNS_Resolver::$usevc flag
  1050.      *
  1051.      * @param string $dname The domain (zone) to transfer
  1052.      * @param string $class The class in which to look for the zone.
  1053.      * @return object Net_DNS_Packet
  1054.      * @access public
  1055.      */
  1056.     function axfr($dname, $class = "IN")
  1057.     {
  1058.         if ($this->debug) {
  1059.             echo ";; axfr_start($dname, $class)\n";
  1060.         }
  1061.         if (! count($this->nameservers)) {
  1062.             $this->errorstring = "no nameservers";
  1063.             if ($this->debug) {
  1064.                 echo ";; ERROR: no nameservers\n";
  1065.             }
  1066.             return(NULL);
  1067.         }
  1068.         $packet = $this->make_query_packet($dname, "AXFR", $class);
  1069.         $packet_data = $packet->data();
  1070.         $ans = $this->send_tcp($packet, $packet_data);
  1071.         return($ans);
  1072.     }
  1073.  
  1074.     /* }}} */
  1075.     /* not completed - Net_DNS_Resolver::read_tcp() {{{ */
  1076.     /**
  1077.      * Unknown - not ported yet
  1078.      */
  1079.     function read_tcp()
  1080.     {
  1081.     }
  1082.  
  1083.     /* }}} */
  1084. }
  1085. /* }}} */
  1086. /* VIM settings {{{
  1087.  * Local variables:
  1088.  * tab-width: 4
  1089.  * c-basic-offset: 4
  1090.  * soft-stop-width: 4
  1091.  * c indent on
  1092.  * expandtab on
  1093.  * End:
  1094.  * vim600: sw=4 ts=4 sts=4 cindent fdm=marker et
  1095.  * vim<600: sw=4 ts=4
  1096.  * }}} */
  1097. ?>
  1098.